/*
 * Wave.java
 *
 * Created on November 25, 2007, 3:15 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package hextd.wave;

import hextd.sprite.EnemyFactory;
import hextd.sprite.EnemySprite;
import hextd.util.Context;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;

/**
 *
 * @author hylke
 */
public class Wave {
    private Vector<EnemySprite> enemies = new Vector<EnemySprite>();
    private Vector<String[]> nameStrings = new Vector<String[]>();
    private Vector<Integer> paths = new Vector<Integer>();
    private long baseHealth;
    private int basePrice;
    private boolean finalised = false;
    private Context context;
    private int level;
    
    private Hashtable<EnemyInstance, Integer> enemyCounts = new Hashtable<EnemyInstance, Integer>();
    
    public class EnemyInstance implements Comparable<EnemyInstance> {
        public EnemyFactory.Enemy   type;
        public EnemySprite.Size     size;
        public int                  price;
        public long                 health;
        
        public EnemyInstance(EnemyFactory.Enemy t, EnemySprite.Size s, long h, int price) {
            this.type = t;
            this.size = s;
            this.health = h;
            this.price = price;
        }
        public int compareTo(EnemyInstance o) {
            if (o.type == this.type && o.size == this.size && o.price == this.price && o.health == this.health) {
                return 0;
            } else if (this.health > o.health) {
                return 1;
            } else {
                return -1;
            }
        }

    }
    
    /** Creates a new instance of Wave */
    public Wave(Context context, long baseHealth, int basePrice, int level) {
        this.baseHealth = baseHealth;
        this.basePrice = basePrice;
        this.context = context;
        this.level = level;
    }
    
    public void addEnemiesFromNames(String[] names, int path) {
        this.nameStrings.add(names);
        this.paths.add(new Integer(path));
    }
    
    private void countEnemy(EnemyInstance e) {
        Integer i = null;
        for (EnemyInstance o : enemyCounts.keySet()) {
            if (o.compareTo(e) == 0) {
                i = enemyCounts.get(o);
                e = o;
                break;
            }
        }
         
        if (i==null) {
            i = new Integer(0);
        }
        i++;
        enemyCounts.put(e, i);
    }
    
    public Set<EnemyInstance> enemySet() {
        if (!this.finalised) {
            this.finalise();
        }
        return enemyCounts.keySet();
    }
    
    public int enemyCount(EnemyFactory.Enemy e) {
        if (!this.finalised) {
            this.finalise();
        }
        Integer i = enemyCounts.get(e);
        if (i == null) {
            return 0;
        }
        return i.intValue();
    }
    public int enemyCount() {
        if (!this.finalised) {
            this.finalise();
        }
        return this.enemies.size();
    }
    
    private void finalise() {
        this.enemies.clear();
        this.enemyCounts.clear();
        EnemyInstance tempInstance;
        for (int j=0; j<this.nameStrings.size(); j++) {
            String[] names = this.nameStrings.get(j);
            int path = this.paths.get(j).intValue();
            int nr = 1;
            int count = 0;
            float prefDel;
            float totDel = this.context.getPath(path).getBaseDelay();
            for (int i=0; i<names.length; i++) {
                if (EnemyFactory.isEnemy(names[i])) {
                    EnemyFactory.Enemy type = EnemyFactory.identifyEnemy(names[i]);
                    EnemySprite.Size s = EnemyFactory.getSize(names[i]);
                    
                    double healthMod = 1;
                    float delayMod = 1;
                    int number;
                    switch (s) {
                        case large:
                            number = Math.round(0.49f+((float)nr)/10f);
                            healthMod = 1.0*nr / number;
                            delayMod = s.scaleMod;
                            nr = number;
                            break;
                        case small:
                            number = nr*10;
                            healthMod = 0.2;
                            delayMod = 0.1f;
                            nr = number;
                            break;
                    }
                    int price = Math.max(1, (int)Math.round(this.basePrice * healthMod));
                    long health = Math.round(this.baseHealth*healthMod);
                    
                    tempInstance = new EnemyInstance(type, s, health, price);
                    this.countEnemy(tempInstance);
                    
                    // Now we generate the enemies.
                    for (int e=0; e<nr; e++) {
                        int eCount = enemies.size();
                        if (eCount>0) {
                            // The preffered delay is the maximum of that of this unit, and the unit before it.
                            prefDel = delayMod * Math.max(EnemyFactory.identifyEnemy(names[i]).getPreferredDelay(), enemies.get(eCount-1).getPreferredDelay());
                        } else {
                            prefDel = delayMod * EnemyFactory.identifyEnemy(names[i]).getPreferredDelay();
                        }
                        totDel += prefDel;
                        enemies.add(EnemyFactory.getEnemy(names[i], this.context, path, totDel, health, price));
                        count++;
                    }
                    
                    nr = 1;
                } else {
                    try {
                        nr = new Integer(names[i]);
                    } catch (NumberFormatException ex) {
                        ex.printStackTrace();
                        nr = 1;
                    }
                }
            }
        }
        this.finalised = true;
    }
    
    public EnemySprite[] getEnemies() {
        this.finalise();
        return enemies.toArray(new EnemySprite[enemies.size()]);
    }
    
    public long getBaseHealth() {
        return baseHealth;
    }
    
    public int getBasePrice() {
        return basePrice;
    }

    public int getLevel() {
        return level;
    }

    public Vector<String[]> getNameStrings() {
        return this.nameStrings;
    }
    
}
